#include "appState.h"

AppState::AppState( MainWindow *mw, SoapHandler *sh )
{ mainWin = mw;
  soapHnd = sh;

  // Define the top-level state machine
  QState      *init     = new QState( this );                         init   ->setObjectName( "init"    );
  QState      *running  = new QState( QState::ParallelStates, this ); running->setObjectName( "running" );
  QFinalState *closing  = new QFinalState( this );                    closing->setObjectName( "closing" );
  init   ->assignProperty( mainWin->ui->appState, "text", "init"    );
  running->assignProperty( mainWin->ui->appState, "text", "running" );
  setInitialState( init );
  init   ->addTransition( running );
  running->addTransition( mainWin, SIGNAL( closing() ), closing );

  // Initalize sub-state machines
  defineLocation( new QState( running ) );
  defineForecast( new QState( running ) );
  defineImageGen( new QState( running ) );
  defineCalendar( new QState( running ) );
  defineRadar(    new QState( running ) );
  dumpObjectTree();
  start();
}

void AppState::defineRadar( QState *ps )
{                                                    ps     ->setObjectName( "radar "  );
  LocDepState *waiting = new      LocDepState( ps ); waiting->setObjectName( "waiting" );
       QState *getting = new           QState( ps ); getting->setObjectName( "getting" );
    WaitState *drawing = new WaitState( 60000, ps ); drawing->setObjectName( "drawing" );

  waiting->assignProperty( mainWin->ui->radarState, "text", "waiting" );
  getting->assignProperty( mainWin->ui->radarState, "text", "getting" );
  drawing->assignProperty( mainWin->ui->radarState, "text", "drawing" );
  ps->setInitialState( waiting );
  waiting->addTransition( waiting, SIGNAL(           changed() ), getting );
  waiting->addTransition( soapHnd, SIGNAL(    updateForecast() ), getting );
  getting->addTransition( soapHnd, SIGNAL( gotRadarImageDone() ), drawing );
  drawing->addTransition( drawing, SIGNAL(       waitingDone() ), waiting );
  connect( soapHnd, SIGNAL( latLonChange(QString) ), waiting, SLOT( newLatLon(QString) ) );
  connect( getting, SIGNAL(             entered() ), soapHnd, SLOT( getRadarImage()    ) );
  connect( soapHnd, SIGNAL(         drawingDone() ), drawing, SLOT( go()               ) );
}

void AppState::defineLocation( QState *ps )
{                                        ps      ->setObjectName( "location " );
  QState *invalid    = new QState( ps ); invalid ->setObjectName( "invalid"   );
  QState *checking   = new QState( ps ); checking->setObjectName( "checking"  );
  QState *valid      = new QState( ps ); valid   ->setObjectName( "valid"     );
  invalid ->assignProperty( mainWin->ui->locationState, "text", "invalid"  );
  checking->assignProperty( mainWin->ui->locationState, "text", "checking" );
  valid   ->assignProperty( mainWin->ui->locationState, "text", "valid"    );
  ps->setInitialState( invalid );
  invalid ->addTransition( mainWin->ui->zipCode, SIGNAL( textChanged( QString ) ), checking );
  valid   ->addTransition( mainWin->ui->zipCode, SIGNAL( textChanged( QString ) ), checking );
  checking->addTransition( soapHnd             , SIGNAL(             zipValid() ), valid    );
  checking->addTransition( soapHnd             , SIGNAL(           zipInvalid() ), invalid  );
  connect( checking, SIGNAL( entered() ), soapHnd, SLOT( checkZip() ) );
}

void AppState::defineForecast( QState *ps )
{                                                      ps     ->setObjectName( "forecast" );
  LocDepState *waiting = new LocDepState(        ps ); waiting->setObjectName( "waiting"  );
       QState *getting = new      QState(        ps ); getting->setObjectName( "getting"  );
    WaitState *holding = new   WaitState( 60000, ps ); holding->setObjectName( "holding"  );
  soapHnd->forecastWaiting = waiting;
  waiting->assignProperty( mainWin->ui->forecastState, "text", "waiting" );
  getting->assignProperty( mainWin->ui->forecastState, "text", "getting" );
  holding->assignProperty( mainWin->ui->forecastState, "text", "holding" );
  ps->setInitialState( waiting );
  waiting->addTransition( waiting, SIGNAL(        changed() ), getting );
  waiting->addTransition( soapHnd, SIGNAL( updateForecast() ), getting );
  getting->addTransition( soapHnd, SIGNAL(   forecastDone() ), holding );
  holding->addTransition( holding, SIGNAL(    waitingDone() ), waiting );
  connect( soapHnd, SIGNAL( latLonChange(QString) ), waiting, SLOT( newLatLon(QString) ) );
  connect( soapHnd, SIGNAL(         drawingDone() ), holding, SLOT(               go() ) );
  connect( getting, SIGNAL(             entered() ), soapHnd, SLOT(    checkForecast() ) );
}

void AppState::defineCalendar( QState *ps )
{                                          ps   ->setObjectName( "calendar" );
  QState *init         = new QState( ps ); init ->setObjectName( "init"     );
  QState *query        = new QState( ps ); query->setObjectName( "query"    );
  ps->setInitialState( init );
}

void AppState::defineImageGen( QState *ps )
{                                           ps     ->setObjectName( "imageGen" );
  WaitState *waiting = new WaitState( ps ); waiting->setObjectName( "waiting"  );
  WaitState *waitRad = new WaitState( ps ); waitRad->setObjectName( "waitRad"  );
  QState    *draw    = new    QState( ps ); draw   ->setObjectName( "draw"     );
  QState    *upload  = new    QState( ps ); upload ->setObjectName( "upload"   );
  waiting->assignProperty( mainWin->ui->imageGenState, "text", "waiting" );
  draw   ->assignProperty( mainWin->ui->imageGenState, "text", "draw"    );
  upload ->assignProperty( mainWin->ui->imageGenState, "text", "upload"  );
  ps->setInitialState( waiting );
  waiting->addTransition( waiting, SIGNAL( waitingDone() ), waitRad );
  waitRad->addTransition( waitRad, SIGNAL( waitingDone() ), draw    );
  draw   ->addTransition( soapHnd, SIGNAL( drawingDone() ), upload  );
  upload ->addTransition( soapHnd, SIGNAL(  uploadDone() ), waiting );
  connect( soapHnd, SIGNAL(       newForecast() ), waiting, SLOT(          go() ) );
  connect( soapHnd, SIGNAL( gotRadarImageDone() ), waitRad, SLOT(          go() ) );
  connect( draw   , SIGNAL(           entered() ), soapHnd, SLOT(   drawImage() ) );
  connect( upload , SIGNAL(           entered() ), soapHnd, SLOT( uploadImage() ) );
}

///////////////////////////////////////////////////////////////////////////////////////////
// LocDepState - location dependent states
// These states normally exit when a new latLon is received
// if a new latLon is received when the state is "away," the changed() signal will emit onEntry
// normally, this changed() signal would be hooked up to a transition to a state dealing with
// the new location
LocDepState::LocDepState( QState *parent ) : QState( parent )
{ latLonChanged = false; }

void LocDepState::newLatLon( QString newLL )
{ if ( latLon != newLL )
    { latLonChanged = true;
      latLon        = newLL;
      emit changed();
    }
}

void LocDepState::onEntry( QEvent * )
{ if ( latLonChanged )
    emit changed();
}

void LocDepState::onExit( QEvent * )
{ latLonChanged = false; }

//////////////////////////////////////////////////////////////////////////
// WaitState implements a simple latch, if the go() slot is activated,
//   wait will emit done() immediately, and again onEntry.  done() should
//   be connected to a transition to the next state.
WaitState::WaitState( QState *parent ) : QState( parent )
{ dontWait = false;
  maxTime = 0;
}

WaitState::WaitState( int t, QState *parent ) : QState( parent )
{ dontWait = false;
  maxTime = t;
  connect( &timer, SIGNAL( timeout() ), this, SLOT( go() ) );
}

void WaitState::onEntry( QEvent * )
{ if ( dontWait )
    emit waitingDone();
   else
    if ( maxTime > 0 )
      timer.start( maxTime );
}

void WaitState::onExit( QEvent * )
{ dontWait = false;
  timer.stop();
}

void WaitState::go()
{ dontWait = true;  // For when this state is not active
  emit waitingDone();      // For when this state is active
}
